#include "netlib/base/logger.h"

#include <cerrno>
#include <cstring>

// using namespace netlib;
// using namespace netlib::detail;

#include "netlib/base/current_thread.h"

__thread char t_time_str[18]{0};
__thread time_t last_seconds{0};

const char* netlib::StrerrorTl(int save_errno) {
	static __thread char t_errno_buf[512]{0};
	return strerror_r(save_errno, t_errno_buf, sizeof(t_errno_buf));
}
namespace netlib {
Logger::LogLevel InitLogLevel() {
	if (::getenv("NETLIB_LOG_TRACE")) {
		return Logger::kTrace;
	}
	if (::getenv("NETLIB_LOG_DEBUG")) {
		return Logger::kDebug;
	}
	return Logger::kInfo;
}

Logger::LogLevel Logger::min_level = InitLogLevel();

netlib::Logger::Logger(int line, BaseName&& file, LogLevel level) :
    line_(line), basename_(file), level_(level) {
	FormatHeader();
}

netlib::Logger::Logger(int line, BaseName&& file, bool is_abort) :
    line_(line), basename_(file), level_(is_abort ? kFatal : kError) {
	int save_errno = errno;
	FormatHeader();
	if (save_errno != 0) {
		stream_ << StrerrorTl(save_errno) << " (errno=" << save_errno << ") ";
	}
}

void netlib::Logger::FormatHeader() {
	static const char* level_names[]{"TRACE ", "DEBUG ", "INFO  ", "WARN  ", "ERROR ", "FATAL "};
	UpdateThreadTime();
	stream_ << LogStream::KnowSize(t_time_str, sizeof(t_time_str) - 1) << " "
	        << LogStream::KnowSize(current_thread::TidStr(), current_thread::TidStrLength()) << " "
	        << level_names[level_];
}

void netlib::Logger::UpdateThreadTime() {
	time_t seconds(time_.SecondsSinceEpoch());
	if (seconds != last_seconds) {
		last_seconds = seconds;

		struct tm* tm_time = localtime(&seconds);

		int len = snprintf(t_time_str, sizeof(t_time_str), "%4d%02d%02d %02d:%02d:%02d",
		                   tm_time->tm_year + 1900, tm_time->tm_mon + 1, tm_time->tm_mday,
		                   tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec);
		assert(len > 0);
	}
}

void netlib::Logger::FormatTail() { stream_ << " - " << basename_ << ":" << line_ << "\n"; }

netlib::Logger::~Logger() {
	FormatTail();
	stream_.Output();
	if (level_ == kFatal) {
		netlib::LogStream::Flush();
		abort();
	}
}
} // namespace netlib
